home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #29 (Feb 88) / scsi forth driver / Scsidisk2.fth
Text File  |  1987-12-18  |  24KB  |  904 lines

  1. only forth definitions
  2. also mac also assembler
  3.  
  4. CODE SCALE
  5.     MOVE.L   (A6)+,D0
  6.     BMI.S    @1
  7.     MOVE.L   (A6),D1
  8.     ASL.L    D0,D1
  9.     MOVE.L   D1,(A6)
  10.     RTS
  11. @1  MOVE.L   (A6),D1
  12.     NEG.L    D0
  13.     ASR.L    D0,D1
  14.     MOVE.L   D1,(A6)
  15.     RTS
  16. END-CODE
  17.  
  18. : 4ASCII 0
  19.     4 0 DO    8 SCALE 0 WORD 1+ C@ +    LOOP
  20. ;
  21.  
  22. ( *** compiler support words for external definitions *** )
  23. : :xdef 
  24.     create     -4 allot
  25.         $4EFA w, ( JMP )
  26.         0 w,     ( entry point to be filled later )
  27.         0 ,      ( length of routine to be filled later )
  28.         here 6 - 76543
  29. ;
  30.  
  31. : ;xdef { branch marker entry | -- }
  32.     marker 76543 <> abort" xdef mismatch"
  33.     entry branch - branch w!
  34.     here branch - 2+ branch 2+ !
  35.     
  36. : xlen 4 + @ ; ( get length word of external definition )
  37.  
  38. ( *** driver header block *** )
  39.  
  40.  0 CONSTANT drvrFlags
  41.  2 CONSTANT drvrdelay 
  42.  4 CONSTANT drvrEMask
  43.  6 CONSTANT drvrMenu
  44.  8 CONSTANT drvrOpen
  45. 10 CONSTANT drvrPrime
  46. 12 CONSTANT drvrCtl
  47. 14 CONSTANT drvrStatus
  48. 16 CONSTANT drvrClose
  49. 18 CONSTANT drvrname
  50. 50 CONSTANT DAlength
  51.  
  52. ( *** compiler support words for DA and driver definitions *** )
  53. : :DA 
  54.     create     -4 allot
  55.         here 87654 ( start of DA block, and marker )
  56.         54 allot   ( length of block )
  57. ;
  58.  
  59. : ;DA { DAstart marker Ropen Rprime Rctl Rstatus Rclose
  60.         Rflags Rdelay Remask Rmenu Rname | -- }
  61.     marker 87654 <> abort" DA definition mismatch"
  62.     Ropen     DAStart - DAStart drvrOpen + w!
  63.     Rprime     DAStart - DAStart drvrPrime + w!
  64.     Rctl         DAStart - DAStart drvrCtl + w!
  65.     Rstatus     DAStart - DAStart drvrStatus + w!
  66.     Rclose     DAStart - DAStart drvrClose + w!
  67.     Rflags          DAStart drvrFlags + w!
  68.     Rdelay          DAStart drvrDelay + w!
  69.     Remask          DAStart drvrEmask + w!
  70.     RMenu              DAStart drvrMenu + w!
  71.     Rname count dup      DAStart drvrName + c!
  72.         DAStart drvrName + 1+ swap 
  73.         dup 31 > if drop 31 then cmove 
  74.     here DAstart -       DAStart DAlength + !
  75.     
  76. : DAlen DAlength + @ ; ( get length word of external definition )
  77.  
  78. \ —————————————————————————————————————
  79. \ some macros needed in the driver
  80. \ —————————————————————————————————————
  81.  
  82. CODE xchg ( exchange word halves on top of stack )
  83.     move.l (a6)+,d1
  84.     swap.w d1
  85.     move.l d1,-(a6)
  86.     rts
  87. END-CODE MACH
  88.     
  89. CODE min
  90.     MOVE.L    (A6)+,D0                            
  91.     CMP.L     (A6),D0                             
  92.     BGE.S     @1        
  93.     MOVE.L    D0,(A6)                             
  94. @1    RTS 
  95. END-CODE MACH
  96.  
  97. CODE shl ( data #bits )
  98.     MOVE.L (A6)+,D0
  99.     MOVE.L (A6),D1
  100.     LSL.L  D0,D1
  101.     MOVE.L D1,(A6)
  102.     RTS
  103. END-CODE    MACH
  104.  
  105. CODE shr ( data #bits )
  106.     MOVE.L (A6)+,D0
  107.     MOVE.L (A6),D1
  108.     LSR.L  D0,D1
  109.     MOVE.L D1,(A6)
  110.     RTS
  111. END-CODE    MACH
  112.  
  113. CODE w*
  114.     MOVE.L (A6)+,D1
  115.     MOVE.L (A6)+,D0
  116.     MULS.W D1,D0
  117.     MOVE.L D0,-(A6)
  118.     RTS
  119. END-CODE MACH
  120.  
  121. \ —————————————————————————————————————
  122. \ **** DA glue macros
  123. \ —————————————————————————————————————
  124.  
  125. $8FC CONSTANT JioDone 
  126.  
  127. CODE DA.prelude
  128.     LINK    A6,#-512         ( 512 bytes of local Forth stack )
  129.     MOVEM.L A0-A1,-(A7)        ( save registers )
  130.     MOVE.L  A6,A3            ( setup local loop return stack )
  131.     SUBA.L  #256,A3            ( in the low 256 local stack bytes )
  132.     MOVE.L  A0,-(A6)         ( parameter block )
  133.     MOVE.L  A1,-(A6)        ( device control entry )
  134.     RTS            \ just to indicate the MACHro stops here 
  135. END-CODE MACH
  136.  
  137. CODE DA.epilogue
  138.     MOVE.L  (A6)+,D0        ( return code )
  139.     MOVEM.L (A7)+,A0-A1    ( restore registers )
  140.     UNLK    A6
  141.     RTS
  142. END-CODE MACH
  143.  
  144. CODE DA.JIODone
  145.     MOVE.L  (A6)+,D0        ( return code )
  146.     MOVEM.L (A7)+,A0-A1    ( restore registers )
  147.     UNLK    A6
  148.     move.l    JIODone,A0
  149.     movem.l d4-d7/a4-a6,-(a7)
  150.     jsr        (a0)
  151.     movem.l    (a7)+,d4-d7/a4-a6
  152.     RTS
  153. END-CODE MACH
  154.  
  155. .trap    _newptr,sys,clr    $A71E
  156.  
  157. \ fields of device control entry
  158.  4 CONSTANT dCtlFlags
  159.  6 CONSTANT dCtlQHdr
  160. 16 CONSTANT dCtlPosition
  161. 20 CONSTANT dCtlStorage
  162. 24 CONSTANT dCtlRefNum
  163. 26 CONSTANT dCtlCurTicks
  164. 30 CONSTANT dCtlWindow
  165. 34 CONSTANT dCtlDelay
  166. 36 CONSTANT dCtlEMask
  167. 38 CONSTANT dCtlMenu
  168.  
  169. \ parameter block constants
  170.  
  171. 0   CONSTANT  qLink        \ pointer to next queue entry [long word]
  172. 4   CONSTANT  qType        \ queue type [word]
  173. 6   CONSTANT  ioTrap        \ routine trap [word]
  174. 7   CONSTANT  ioTrap+1        \ read or write command
  175. 8   CONSTANT  ioCmdAddr        \ routine address [long word]
  176. 12  CONSTANT  ioCompletion    \ addr of completion routine [long word]
  177. 16  CONSTANT  ioResult        \ result code returned here [word]
  178. 18  CONSTANT  ioNamePtr        \ holds pointer to file name string or
  179. 22  CONSTANT  ioVRefNum        \ volume reference number
  180. 26  CONSTANT  csCode        ( type of control call )
  181. 28  CONSTANT  csParam        ( control call parameters )
  182.  
  183. \ MFS I/O Parameter Block
  184. 24 CONSTANT    ioRefNum
  185. 26 CONSTANT    ioVersNum
  186. 27 CONSTANT    ioPermssn
  187. 28 CONSTANT    ioMisc
  188. 32 CONSTANT    ioBuffer
  189. 36 CONSTANT    ioReqCount
  190. 40 CONSTANT    ioActCount
  191. 44 CONSTANT    ioPosMode
  192. 46 CONSTANT    ioPosOffset
  193. 50 CONSTANT IOParamBlkSize
  194.  
  195. 4ascii SDRV constant "sdrv
  196. 4ascii TFS1 constant "tfs1
  197.  
  198.  
  199. \ Equates
  200. \ My excuses for the format. This has been taken almost
  201. \ 'as is' from Apple's SCSI driver example. - jl -
  202.  
  203. EQU    verChar    $34    \ version '4'
  204. EQU    SCSIZE    10     \ size of SCSI extended command
  205.  
  206. \ Equates for our storage (pointed to by DCtlStorage)
  207. EQU    Offset    0               \ [long] offset of starting sector
  208. EQU    MyDQEl        Offset+4        \ [20 bytes] drive queue element (with flags) for this drive
  209. EQU    MyDrvNum    MyDQEl+20       \ [word] drive num (determined by scanning drive queue)
  210. EQU    NextAddr    MyDrvNum+2      \ [long] ptr to current block buffer
  211. EQU    TickleFlag    NextAddr+4      \ [byte] Do we need to remind the system about this drive?
  212. EQU    BlindOK        TickleFlag+1    \ [byte] Can we use blind reads?
  213.             \ I left this in to keep the format the same. 
  214.             \ We don't need it since our driver does not read blind.
  215.  
  216. EQU    SCmd        BlindOK+1       \ [10 bytes] SCSI extended cmd Block /JL
  217. EQU    StatWord    SCmd+10         \ [word] status and message bytes...
  218. EQU    MsgWord        StatWord+2      \ [word] ... returned by SCSIComplete
  219.  
  220. EQU    OurID        MsgWord+2 \ [word] our SCSI ID
  221. EQU    SCSIPseudo    OurID+2         \ [30 bytes] SCSI pseudo-code program - three instructions long
  222. EQU    SCSIPar1    SCSIPseudo+2    \ first SCSI code parameter (long)
  223. EQU    SCSIPar2    SCSIPar1+4        \ 2nd SCSI code parameter (long)
  224. EQU    DiskVarLth    SCSIPseudo+(SCSIZE*3) \ length of our locals . . .
  225. EQU DQDrvSize    12
  226.  
  227. EQU realSize    MyDQEl+DQDrvSize+4
  228.  
  229. \ equates for CSParam offsets for our special control call
  230. EQU    DSCCmd        CSParam        \ Ptr to SCSI command block
  231. EQU    DSCPseudo    DSCCmd+4        \ Ptr to SCSI pseudocode (if any bytes to xfer)
  232. EQU    DSCBuffer    DSCPseudo+4        \ Ptr to buffer for transfer (if any)
  233. EQU    DSCSize        DSCBuffer+4        \ Size of transfer, signed (+ if read, - if write)
  234. EQU    DSCTicks    DSCSize+4        \ Tick count we're willing to wait for completion
  235. EQU    DSCCmdSize    DSCTicks+4        \ (word) Size of command block we're sending (usually 6)
  236.  
  237. EQU KillCode    1 
  238. EQU    VerifyCode    5
  239. EQU    FormatCode    6
  240. EQU    EjectCode    7
  241. EQU    IconCode    21
  242. EQU AccRun        65
  243. EQU    SCSICode    77      \ our own special code (defined above)
  244.  
  245. EQU    ControlErr    -1
  246. EQU    StatusErr    -1
  247. EQU    ParamErr    -50
  248. EQU    nsDrvErr    -56
  249. EQU    nsVErr        -35
  250. EQU    ioErr        -36
  251.  
  252. EQU    dNeedTime    $DFFF    \ to clear bit 5 of high byte in drvrFlags
  253.  
  254. EQU    DiskInsertEvt    7
  255. EQU SysEvtMask    $144
  256. EQU UTableBase    $11C
  257. EQU DrvQHdr        $308
  258. EQU QHead        $2
  259.  
  260. EQU    DQDrive        6
  261. EQU    DQRefNum    8
  262. EQU    DQFSid        10
  263.  
  264. EQU    PDSig        0
  265. EQU    PDSigWord    $5453
  266. EQU    PDFSID        8
  267. EQU    PDLen        12
  268.  
  269. \ _______________________________________________________________________
  270. \ The code starts here.
  271. \ _______________________________________________________________________
  272.  
  273. :XDEF    ScsiDisk  \ compiles a jump to the install code at the end 
  274.           \ which will be resolved at the end of the definition.
  275.  
  276. :DA    DiskDrvr  \ this word provides the driver header structure
  277.         .ALIGN
  278.  
  279. \ Q200 Icon, as given by Quantum
  280. \ If you find this 'snail' ugly, feel free to change it ...
  281. \  J.L.
  282.  
  283. header  SCSIIcon        
  284.         DC.L $00000000 DC.L $00000000 DC.L $00000000 DC.L $000FF000        
  285.         DC.L $003FFC00 DC.L $00FFFF00 DC.L $01FFFF80 DC.L $03F81FC0        
  286.         DC.L $07E007E0 DC.L $07C003E0 DC.L $0F8001F0 DC.L $0F0000F0 
  287.         DC.L $1F0000F8 DC.L $1E000078 DC.L $1E000078 DC.L $1E000078 
  288.         DC.L $1E000078 DC.L $1E000078 DC.L $1E000078 DC.L $1F0000F8
  289.         DC.L $0F0000F0 DC.L $0F8001F0 DC.L $07C003E0 DC.L $07E007E0
  290.         DC.L $03F80000 DC.L $01FFFFF0 DC.L $00FFFFF8 DC.L $003FFFF8
  291.         DC.L $000FFFF8 DC.L $00000000 DC.L $00000000 DC.L $00000000
  292.         DC.L $00000000 DC.L $00000000 DC.L $000FF000 DC.L $003FFC00
  293.         DC.L $00FFFF00 DC.L $01FFFF80 DC.L $03FFFFC0 DC.L $07FFFFE0
  294.         DC.L $0FFFFFF0 DC.L $0FFFFFF0 DC.L $1FFFFFF8 DC.L $1FFFFFF8
  295.         DC.L $3FFFFFFC DC.L $3FFFFFFC DC.L $3FFFFFFC DC.L $3FFFFFFC
  296.         DC.L $3FFFFFFC DC.L $3FFFFFFC DC.L $3FFFFFFC DC.L $3FFFFFFC
  297.         DC.L $1FFFFFF8 DC.L $1FFFFFF8 DC.L $0FFFFFF0 DC.L $0FFFFFF0
  298.         DC.L $07FFFFF0 DC.L $03FFFFF8 DC.L $01FFFFFC DC.L $00FFFFFC
  299.         DC.L $003FFFFC DC.L $000FFFFC DC.L $00000000 DC.L $00000000
  300.  
  301. \ Our "Where:" string
  302.         DC.B    11
  303.         DC.B    'Q200 (SCSI)'
  304.         .ALIGN
  305.  
  306. \ SCSI handler glue routines
  307. CODE SCSIReset ( -- result code )
  308.     CLR.W -(A7)
  309.     MOVE.W #0,-(A7)
  310.     _SCSIDispatch
  311.     MOVE.W (A7)+,D0
  312.     EXT.L    D0
  313.     MOVE.L D0,-(A6)
  314.     RTS
  315. END-CODE
  316.  
  317. CODE SCSIGet ( -- result code )
  318.     CLR.W -(A7)
  319.     MOVE.W #1,-(A7)
  320.     _SCSIDispatch
  321.     MOVE.W (A7)+,D0
  322.     EXT.L    D0
  323.     MOVE.L D0,-(A6)
  324.     RTS
  325. END-CODE
  326.  
  327. CODE SCSISelect ( TargetID -- SCSIErrorResult )
  328.     MOVE.L (A6)+,D0
  329.     CLR.W -(A7)
  330.     MOVE.W D0,-(A7)
  331.     MOVE.W #2,-(A7)
  332.     _SCSIDispatch
  333.     MOVE.W (A7)+,D0
  334.     EXT.L    D0
  335.     MOVE.L D0,-(A6)
  336.     RTS
  337. END-CODE
  338.  
  339. CODE SCSICmd ( buffer count -- SCSIErrorResult )
  340.     MOVE.L (A6)+,D0
  341.     MOVE.L (A6)+,D1
  342.     CLR.W -(A7)
  343.     MOVE.L D1,-(A7)
  344.     MOVE.W D0,-(A7)
  345.     MOVE.W #3,-(A7)
  346.     _SCSIDispatch
  347.     MOVE.W (A7)+,D0
  348.     EXT.L    D0
  349.     MOVE.L D0,-(A6)
  350.     RTS
  351. END-CODE
  352.  
  353. CODE SCSIComplete ( waitTicks mess stat -- SCSIErrorResult )
  354.     CLR.W -(A7)
  355.     MOVE.L (A6)+,-(A7)
  356.     MOVE.L (A6)+,-(A7)
  357.     MOVE.L (A6)+,-(A7)
  358.     MOVE.W #4,-(A7)
  359.     _SCSIDispatch
  360.     MOVE.W (A7)+,D0
  361.     EXT.L    D0
  362.     MOVE.L D0,-(A6)
  363.     RTS
  364. END-CODE
  365.  
  366. 1 CONSTANT SCInc    2 CONSTANT SCnoInc
  367. 3 CONSTANT SCAdd    4 CONSTANT SCMove
  368. 5 CONSTANT SCLoop    6 CONSTANT SCNop
  369. 7 CONSTANT SCStop    8 CONSTANT SCComp
  370.  
  371. \ ———————————————————————————————
  372. \ main driver routines start here
  373. \ ———————————————————————————————
  374.  
  375. : SCSICommon 
  376.         \ written to emulate the SCSICommon 
  377.         \ routine in Apple's example 
  378.         \ as closely as possible.
  379.     { pseudo cmdblock ourVars ticks bytes cmdsize 
  380.         | writing mess stat -- result }
  381.  
  382.     SCSIGet 0= IF
  383.         ourVars ourID + w@ 
  384.         SCSISelect 0= IF
  385.             cmdBlock cmdSize SCSICmd 0= bytes AND IF
  386.                 pseudo bytes 0< \ bytes <0 if writing
  387.                 IF (call) SCSIWrite drop
  388.                 ELSE (call) SCSIRead drop THEN
  389.         \ Note: Your system may be able to support blind transfers.
  390.         \ Here is the place to experiment with such things ---
  391.             THEN
  392.             ticks ^ mess ^ stat SCSIComplete
  393.             0= IF  
  394.                 stat $FF AND IF ioErr ( there was an SCSI error )
  395.                     ELSE 0 ( successful completion ) THEN
  396.             ELSE ( complete unsuccessful ) ioErr
  397.             THEN
  398.         ELSE ( select unsuccessful ) ioErr
  399.         THEN
  400.     ELSE ( get unsuccessful ) ioErr
  401.     THEN
  402. ;
  403.  
  404. : DiskClose { parblk dce | -- result }
  405.     0 ( result code = OK ) ;
  406.  
  407. : diskControl { parblk dce | ourVars -- result }
  408.     dce DCtlStorage + @ -> ourVars                
  409.     parblk csCode + w@
  410.     CASE
  411.         killCode    OF    0 ENDOF
  412.         verifyCode    OF    0 ENDOF
  413.         formatCode    OF    0 ENDOF
  414.  
  415.         ejectCode    OF
  416.             ourVars MyDrvNum + w@         \ check drive # in request
  417.             parblk  IOVRefNum + w@ =     \ the same?
  418.             IF
  419.                 SysEvtMask w@ IF ( we're not at boot time )
  420.                     DiskInsertEvt
  421.                     MyDrvNum ourVars + w@
  422.                     (call) PostEvent drop
  423.                     ELSE ( boot time )
  424.                     1 ourVars tickleFlag + c!
  425.                     ( drive will be remembered after boot )
  426.                     THEN
  427.                 controlErr
  428.             ELSE nsDrvErr
  429.             THEN
  430.             ENDOF
  431.  
  432.         iconCode OF ['] SCSIIcon parblk csParam + ! 
  433.             0 ENDOF
  434.  
  435.         accRun OF
  436.             ourVars tickleFlag + c@ 
  437.             ourVars offset + @ 0= not 
  438.                 ( we have a good partition )
  439.                 AND
  440.                 IF 
  441.                 DiskInsertEvt
  442.                 MyDrvNum ourVars + w@
  443.                 (call) PostEvent drop
  444.                 THEN
  445.             0 dce DCtlDelay + w!
  446.             dce DCtlFlags + dup w@ 
  447.                 dNeedTime AND swap w! ( clear flag )
  448.             0 ourVars tickleFlag + c!
  449.             0 ENDOF
  450.  
  451.         scsiCode OF
  452.             parblk     dup DSCPseudo + @
  453.                     dup DSCCmd + @
  454.                         ourVars
  455.                     dup DSCTicks + @
  456.                     dup DSCSize + @
  457.                         DSCCmdSize + w@
  458.             SCSICommon
  459.             ENDOF
  460.  
  461.         ( otherwise ) 
  462.         controlErr
  463.     ENDCASE
  464. ;
  465.  
  466. : DiskStatus { parblk dce | -- result } statusErr ;
  467.  
  468. CODE GetSysPtr
  469.     move.l    (a6)+,d0
  470.     _newptr,sys,clr
  471.     move.l    a0,-(a6)
  472.     rts
  473. END-CODE    
  474.  
  475. CODE AddDrv ( dqe refnum drv# | -- )
  476.     move.l    (a6)+,d0
  477.     move.l    (a6)+,d1
  478.     swap.w    d0
  479.     move.w    d1,d0
  480.     move.l    (a6)+,a0
  481.     _AddDrive
  482.     rts
  483. END-CODE
  484.     
  485. : DiskOpen { parblk dce | 
  486.         ourVars thisQElem driveNum dqe SCSIprog -- result }
  487.  
  488.     DiskVarLth GetSysPtr dup         \ get memory for local variables 
  489.     -> ourVars dce DCtlStorage + !     \ and store pointer to it        
  490.     100 5 DO                        \ find unused drive #
  491.         DrvQHdr QHead + @ -> thisQElem    \ scan queue
  492.         BEGIN thisQElem 0= IF i leave THEN    
  493.                         \ end of queue? we have a good number
  494.             thisQElem DQDrive + w@
  495.             i <> WHILE    \ keep scanning as long as # is not in use
  496.             thisQElem ( QLink + ) @ -> thisQElem
  497.         REPEAT
  498.     LOOP    -> driveNum    
  499.     driveNum ourVars myDrvNum + w! \ remember drive # in local vars
  500.  
  501. \ Add a drive to the drive queue. First, some fun facts:
  502. \ The drive queue element starts four bytes before the DQEPtr! These
  503. \ four bytes contain "hardware-locked", "ejectable", and "disk-in-place" info.
  504. \ Not As Interesting But Still True: HFS supports volumes >32MBytes, 
  505. \ but since the dqDrvSize field in the DQE is only a word, the Software Gurus
  506. \ had to resort to bizarre sorcery: If the qType field (formerly unused in
  507. \ DQE's) is 1, the word following the dqDriveSize field is assumed to be
  508. \ the high-order word of a LongInt block count! (dqDriveSize is still the low-
  509. \ order word). It works even if the size doesn't require both words,
  510. \ so we always do it this way.
  511. \
  512. \ See: Tech Note #36.
  513.          
  514.     ourVars MyDQEl + 8 over w!    \ set non-ejectable and clear the rest
  515.         2+ 0 over w! 2+    -> dqe    \ this is the real start of the DQElem
  516.  
  517.     1 dqe qType +    w!    \ large vol queue type
  518.     0 dqe dqDrvSize + !    \ no size yet
  519.     0 dqe dQFSID + w!    \ normal file system
  520.  
  521.     dqe 
  522.     dce DCtlRefNum + w@ 
  523.     driveNum    AddDrv    \ add drive to queue
  524.     
  525.     \ now set up the SCSI pseudo program in driver's local vars
  526.  
  527.     ourVars SCSIPseudo +  -> SCSIprog
  528.     scnoinc SCSIprog w!
  529.     scstop  SCSIprog scsize + w!
  530.     0 \ result code = good
  531. ;
  532.  
  533. : DiskPrime { parblk dce | 
  534.         ourVars sectors bytes start size r/w sect transferred error -- result } 
  535.  
  536.     dce dCtlStorage + @ -> ourVars    \ setup local var pointer
  537.     1 ourVars TickleFlag + c!
  538.  
  539.     \ convert byte count into number of sectors
  540.     parblk IOReqCount + @ 9 shr $1FFFFF AND -> sectors
  541.     \ convert starting position into sector number
  542.     dce dCtlPosition + @ 9 shr $1FFFFF AND -> start
  543.     
  544.     ourVars realSize + @ xchg -> size \ get drive size
  545.     start sectors + size 1+ < IF ( valid request )
  546.         0 -> transferred
  547.         ourVars ( offset + ) @ +> start \ offset by start of partition
  548.  
  549.         parBlk IOTrap+1 + c@ 3 = ( is this a write command? )
  550.         IF  -1 -> r/w $2A00 ( SCSI extended write )
  551.         ELSE 1 -> r/w $2800 ( SCSI extended read ) 
  552.         THEN
  553.             ourVars SCmd + w!    \ put the command away 
  554.  
  555.         BEGIN ( transfer loop )
  556.  
  557.         \ If you have problems getting the SCSI transfer to work
  558.         \ with your particular disk, try changing the number of
  559.         \ sectors transferred on each call ( 127 here )
  560.         \ or change the read/write extended to a normal read/write.
  561.         \ Note that in that case you'll have to change the command 
  562.         \ block setup as well. 
  563.  
  564.         127 sectors min -> sect
  565.         transferred +> start
  566.         parblk IOBuffer + @ transferred 9 shl + 
  567.             ourVars SCSIPar1 + ! 
  568.         sect 9 shl dup -> bytes 
  569.             ourVars SCSIPar2 + !    \ set # of bytes
  570.  
  571.         start ourVars SCmd + 2+ !    
  572.                 \ set starting position in command block
  573.         bytes 2/ ourVars SCmd + 6 + !    \ set # of sectors
  574.         
  575.         IOErr ( preset, in case loop with retry is unsuccessful )
  576.         10 0 DO ( retry max 10 times )
  577.         ourVars SCSIPseudo +
  578.             ourVars SCmd +
  579.             ourVars 60 r/w 10
  580.             SCSICommon 0= IF drop 0 leave THEN
  581.         1 (call) sysbeep \ just for debugging, 
  582.             \ beeps if SCSI did not complete successfully
  583.         LOOP -> error
  584.  
  585.         -127 +> sectors
  586.         sectors 1- 0< 
  587.         UNTIL ( transfer loop )
  588.  
  589.         error dup 0= IF
  590.             parBlk IOReqCount + @ -> bytes
  591.             bytes parBlk IOActCount + ! 
  592.                     \ we transferred the # of bytes requested
  593.             bytes dce DCtlPosition + +!
  594.         THEN
  595.  
  596.     ELSE IOErr
  597.     THEN            
  598. ;
  599.  
  600. CODE DrvrInst ( unitNum | -- )
  601.     move.l    (a6)+,d0
  602.     not.w    d0
  603.     _DrvrInstall
  604.     rts
  605. END-CODE
  606.     
  607. CODE DrvrRem ( unitNum | -- )
  608.     move.l    (a6)+,d0
  609.     not.w    d0
  610.     _DrvrRemove
  611.     rts
  612. END-CODE
  613.  
  614. CODE openMe ( drvrName | result -- )
  615.     \ allocates a parameter block on the A7 stack and calls
  616.     \ the _open trap. This is easier to do in assembly ---
  617.     moveq.l    #(IOParamBlkSize/2)-1,d0
  618. @1    clr.w    -(a7)
  619.     dbra    d0,@1
  620.     move.l    a7,a0
  621.     move.l    (a6)+,IONamePtr(a0)
  622.     _Open
  623.     add.w    #IOParamBlkSize,a7
  624.     move.l    d0,-(a6)
  625.     rts
  626. END-CODE
  627.  
  628. : RealInstall 
  629.     \ This routine is called by the system boot code with 
  630.     \ the SCSI ID of the disk in D5 and a pointer to its
  631.     \ partition map in A0. We therefore need some special glue code. 
  632.     \ Note that Mach2 allows to do the stack parameter / local
  633.     \ variable declaration after this glue code without any problems
  634.     LINK    A6,#-512         ( 512 bytes of local Forth stack )
  635.     MOVEM.L A2-A6/D2-D7,-(A7)        ( save registers )
  636.     MOVE.L  A6,A3            ( setup local loop return stack )
  637.     SUBA.L  #256,A3            ( in the low 256 local stack bytes )
  638.     MOVE.L  A0,-(A6)         ( partition table pointer )
  639.     MOVE.L  D5,-(A6)        ( SCSI ID )
  640.     
  641.     { partition ID | unitNum hdce dce ourVars pt -- }
  642.  
  643.     ID 32 + -> unitNum
  644.     unitNum DrvrInst    \ allocate DCE and install it
  645.     unitNum 4 w* UTableBase @ + @ -> hdce \ dce handle
  646.         hdce @ -> dce    \ get dce pointer
  647.     ['] DiskDrvr dce ( DCtlDriver + ) ! \ put pointer to driver into dce    
  648.     ['] DiskDrvr drvrFlags + w@
  649.         dce DCtlFlags + w!    \ move driver flags, RAMbase should be cleared
  650.     0 dce DCtlDelay + w!        \ no time needed yet
  651.     ['] DiskDrvr drvrEMask + @
  652.         dce DCtlEMask + !    \ move event mask and menu
  653.  
  654.     ['] DiskDrvr drvrName + openMe    \ try to open this driver
  655.         IF ( not OK ) unitNum DrvrRem
  656.            ['] Scsidisk (call) DisposPtr
  657.            bra @1 \ exit hack. 
  658.             \ This is the Mach2 equivalent of the 
  659.             \ Ugly Goto Statement in Pascal. 
  660.             \ Sorry, but it is so much easier this way...
  661.         THEN 
  662.     
  663.     hdce @ -> dce \ deref this handle again, may have changed
  664.     dce dCtlStorage + @ -> ourVars
  665.     ID ourVars ourID + w!
  666.     partition IF 
  667.         \ well, we should have a non-NIL partition at least...
  668.     partition ( PDSig + ) w@ PDSigWord = IF
  669.         \ and it should be a Macintosh one. The NEW Apple drivers
  670.         \ have a different sig word and DPM format that you 
  671.         \ might want to take into account here (see text). 
  672.         partition 2+ -> pt
  673.         BEGIN
  674.         pt PDFSID + @ ?dup WHILE \ otherwise no good partition found
  675.         "tfs1 = 
  676.             IF ( correct file system ID )
  677.                 pt @ ourVars Offset + !
  678.                 pt 4 + @ xchg ( long drive size, hi word <-> lo word )
  679.                     ourVars realSize + !
  680.                 SysEvtMask w@ 0= IF \ we're booting
  681.                     dce dCtlFlags + dup w@ $2000 OR swap w!
  682.                         ( set dNeedTime flag )
  683.                     1 dce dCtlDelay + w!
  684.                     1 ourVars TickleFlag + c!
  685.                     THEN 
  686.             THEN
  687.         12 +> pt
  688.         REPEAT
  689.     THEN THEN
  690.  
  691. @1    UNLK    A2    \ which was used for local variables
  692.     MOVEM.L (A7)+,A2-A6/D2-D7    ( restore registers )
  693.     UNLK    A6
  694.     RTS        \ we stop here; the rest will be inaccessible junk (4 bytes).
  695. ;
  696.     
  697.  
  698. : DrOpen  DA.prelude DiskOpen DA.epilogue ;
  699. : DrClose DA.prelude DiskClose DA.epilogue ;
  700. : DrCtl   DA.prelude DiskControl DA.JIODone  ; 
  701. : DrStatus DA.prelude DiskStatus DA.JIODone ;
  702. : DrPrime DA.prelude DiskPrime DA.JIODone ;
  703.  
  704. ' DrOpen ' DrPrime ' DrCtl ' DrStatus ' DrClose
  705. $6F00 0 0 0 ( flags delay mask menu )
  706. " .SCSIfth" ( name, MUST start with a period ) 
  707. ;DA
  708.  
  709. ' RealInstall ;XDEF
  710.  
  711. \ —————————————————————————————————————————
  712. \ The following routines are to be added or replaced in the 
  713. \ installer program from the previous column. Included is an
  714. \ installer that will directly move the Forth code to disk, without
  715. \ going through a resource, and some code to install the driver 
  716. \ in memory for testing without writing it to the disk. The 
  717. \ DDM and DPM definitions have been changed somewhat to accommodate 
  718. \ the larger driver, and to have the partition start at the same
  719. \ place that Apple's new SCSI driver expects it (so that you can 
  720. \ replace the Forth driver easily by a new Apple driver in case 
  721. \ you are fed up with this hack)
  722. \ Good luck. - JL -
  723. \ —————————————————————————————————————————
  724.  
  725. hex
  726.  
  727. : create.ddm
  728.     ddm 200 0 fill
  729.     4552 ddm w!
  730.     read.cap ddm 2+ w! ( block size )
  731.              ddm 4 + ! ( # of blocks )
  732.     0 ddm 8 + w! ( device type )
  733.     0 ddm A + w! ( device ID )
  734.     10 ddm C + !  ( first data block )
  735.     1 ddm 10 + w! ( one driver to follow )
  736.     4 ddm 12 + ! ( driver start block )
  737.     A ddm 16 + w! ( driver is 10 blocks long )
  738.     1 ddm 18 + w! ( and runs on Macintosh =1 )
  739. ;
  740.  
  741. : create.dpm
  742.     dpm 200 0 fill
  743.     5453 dpm w!
  744.     10 dpm 2+ ! ( starting block of partition )
  745.     read.cap drop 10 - dpm 6 + ! ( # of blocks )
  746.     "tfs1 dpm A + !   ( TFS1 signature )
  747.     0 dpm E + !
  748. ;    
  749.  
  750. decimal
  751.  
  752. : read.ddm
  753.     0 read.blk 2+ w! 0 read.blk 4 + c!
  754.     1 read.blk 5 + c!
  755.     120 read.blk myDisk @ ddm 512 doscsi.r
  756.     2drop
  757. ;
  758.  
  759. : read.dpm
  760.     0 read.blk 2+ w! 1 read.blk 4 + c!
  761.     1 read.blk 5 + c!
  762.     120 read.blk myDisk @ dpm 512 doscsi.r
  763.     2drop
  764. ;
  765.  
  766. : write.ddm
  767.     0 write.blk 2+ w! 0 write.blk 4 + c!
  768.     1 write.blk 5 + c!
  769.     120 write.blk myDisk @ ddm 512 doscsi.w
  770.     2drop
  771. ;
  772.  
  773. : write.dpm
  774.     0 write.blk 2+ w! 1 write.blk 4 + c!
  775.     1 write.blk 5 + c!
  776.     120 write.blk myDisk @ dpm 512 doscsi.w
  777.     2drop
  778. ;
  779.  
  780. : get.sdrv { | length -- length } 
  781.     ['] scsidisk dup 
  782.     xlen dup -> length driver.block swap cmove
  783.     length
  784. ;
  785.  
  786. : write.sdrv { length | sectors }
  787.     0 write.blk 2+ w! 4 write.blk 4 + c!
  788.     length 512 / 1+ dup write.blk 5 + c! -> sectors
  789.     120 write.blk myDisk @ driver.block sectors 512 * doscsi.w
  790.     cr ." Driver written. Stat, Mess = " . .
  791. ;
  792.  
  793. : dmp { block# | -- } ( for easy testing of SCSI disk contents )
  794.     0 read.blk 2+ w! block# read.blk 4 + c!
  795.     1 read.blk 5 + c!
  796.     120 read.blk myDisk @ ddm 512 doscsi.r
  797.     2drop
  798.     ddm 20 dump
  799. ;
  800.     
  801.  
  802. .TRAP   _newptr,sys     $A51E
  803. $308 CONSTANT DQHeader
  804. 6 CONSTANT QTail
  805.  
  806. VARIABLE syshp.drvr
  807.  
  808. : install.driver { | dstart dlength dbytes pointer -- }
  809.     read.ddm 
  810.     ddm 18 +  @ -> dstart
  811.     ddm 22 + w@ -> dlength
  812.     cr ." Driver starts at sector " dstart .
  813.     ."  and is " dlength . ."  sectors long."
  814.     dlength 512 * -> dbytes
  815.     dstart 256 /mod read.blk 2+ w! read.blk 4 + c!
  816.     dlength read.blk 5 + c!
  817.     120 read.blk myDisk @ driver.block dbytes doscsi.r
  818.     cr ." Driver read; stat, mess = " . .
  819.     dbytes    MOVE.L (A6)+,D0
  820.         _newptr,sys ( get memory block in system heap )
  821.         MOVE.L A0,-(A6)     -> pointer
  822.     pointer 
  823.     IF    driver.block pointer dbytes cmove
  824.         pointer syshp.drvr !
  825.         ELSE ." Not enough system heap for installation." cr
  826.         THEN
  827. ;
  828.  
  829. CODE call.driver
  830.     MOVE.L D5,-(A7)
  831.     MOVE.L (A6)+,D5
  832.     MOVE.L (A6)+,A0
  833.     execute
  834.     MOVE.L (A7)+,D5
  835.     RTS
  836. END-CODE
  837.  
  838. : mount.scsi
  839.     install.driver 
  840.     read.dpm
  841.     SysEvtMask @
  842.     0 SysEvtMask !
  843.     syshp.drvr @ dpm myDisk @ call.driver
  844.     SysEvtMask !
  845. ;
  846.  
  847. : zero.scsi 
  848.     DQHeader qTail + @ dQDrive + w@ ( drive # found )
  849.     cr ." Do you want to zero the directory of drive # " dup . ."  ? "
  850.     yesno if " JL's Hard Disk" call DIZero
  851.             cr ." Result code = " . cr 
  852.     then
  853. ;
  854.     
  855.  
  856. : mount 
  857.     cr ." Looking for SCSI devices..."
  858.     get.disk
  859.     cr ." SCSI drive found at address " myDisk @ .
  860.     cr show.cap
  861.     cr ." format disk? " 
  862.     yesno IF cr ." Do you REALLY want to erase this SCSI disk? "
  863.         yesno IF cr ." Reformatting disk... " 
  864.                  format 
  865.               THEN
  866.         THEN
  867.     modenoattn
  868.     create.ddm    create.dpm
  869.     write.ddm    write.dpm
  870.     cr ." Device and partition descriptor maps written. "
  871.     get.sdrv
  872.     cr ." Writing driver ... "
  873.     write.sdrv
  874.     mount.scsi
  875.     zero.scsi 
  876. ;
  877.  
  878. : install.mem { | dbytes pointer -- }
  879.     get.sdrv
  880.     ['] scsidisk xlen dup -> dbytes
  881.     MOVE.L (A6)+,D0
  882.         _newptr,sys ( get memory block in system heap )
  883.         MOVE.L A0,-(A6)     -> pointer
  884.     pointer 
  885.     IF    driver.block pointer dbytes cmove
  886.         pointer syshp.drvr !
  887.         ELSE ." Not enough system heap for installation." cr
  888.         THEN
  889. ;
  890.  
  891. : mount.mem
  892.     install.mem
  893.     read.dpm
  894.     SysEvtMask @
  895.     0 SysEvtMask !
  896.     syshp.drvr @ dpm myDisk @ call.driver
  897.     SysEvtMask !
  898. ;
  899.  
  900.